<template>
    <div class="hotkeys-page">
        <!-- ================= 顶部筛选工具条 ================= -->
        <div class="toolbar">
            <div class="left-group">
                <!-- 关键字搜索 -->
                <div class="search-wrap">
                    <a-input v-model:value="searchKeyword" class="search-input" placeholder="输入【查询词】搜索" allow-clear
                        @keyup.enter="onSearchClick" @change="onInputChange" />
                    <a-button type="primary" class="search-btn" :loading="loading && searchLoadingLock"
                        @click="onSearchClick">
                        搜索
                    </a-button>
                </div>

                <!-- 列表页 -->
                <span class="label">统计时段：</span>
                <a-radio-group v-model:value="timeRange" class="time-radio" @change="onTimeRangeChange">
                    <a-radio-button value="24h">24小时</a-radio-button>
                    <a-radio-button value="7d">7天</a-radio-button>
                    <a-radio-button value="30d">30天</a-radio-button>
                    <a-radio-button value="custom">自定义</a-radio-button>
                </a-radio-group>
l
                <transition name="fade">
                    <div v-if="timeRange === 'custom'" class="custom-range">
                        <a-range-picker v-model:value="customRange" format="YYYY-MM-DD" :allowClear="false" />
                        <a-button type="primary" ghost class="ml8" @click="applyCustomRange">应用</a-button>
                    </div>
                </transition>
            </div>

            <div class="right-group">
                <a-button type="default" @click="onExport">导出</a-button>
            </div>
        </div>

        <!-- ================= 表格卡片 ================= -->
        <div class="table-card">
            <a-table :data-source="pageData" :columns="columns" row-key="id" bordered :pagination="false"
                :row-selection="rowSelection" :scroll="tableScroll" :loading="loading" class="hot-table"
                :customRow="onCustomRow">
                <template #bodyCell="{ column, index, record }">
                    <!-- 序号 -->
                    <template v-if="column.key === 'rank'">
                        <span
                            :class="['rank', { top3: (pageState.current - 1) * pageState.pageSize + index + 1 <= 3 }]">
                            {{ (pageState.current - 1) * pageState.pageSize + index + 1 }}
                        </span>
                    </template>

                    <!-- 词条名 -->
                    <template v-else-if="column.dataIndex === 'keyword'">
                        <a-typography-text ellipsis :title="record.keyword">{{ record.keyword }}</a-typography-text>
                    </template>

                    <!-- 数值列 -->
                    <template v-else-if="column.key === 'searchCount'">{{ toThousands(record.searchCount) }}</template>
                    <template v-else-if="column.key === 'recall'">{{ toThousands(record.recall) }}</template>
                    <template v-else-if="column.key === 'ctr'">{{ record.ctr }}</template>
                    <template v-else-if="column.key === 'hit'">{{ record.hit }}</template>

                    <!-- 操作 -->
                    <template v-else-if="column.key === 'actions'">
                        <a-space :size="8" split="|">
                            <a-typography-link class="op-link" @click.stop="onCard(record)">卡片</a-typography-link>
                            <a-typography-link class="op-link" @click.stop="onSort(record)">排序</a-typography-link>
                        </a-space>
                    </template>
                </template>
            </a-table>

            <!-- 底部工具条 -->
            <div class="table-footer">
                <div class="footer-left">
                    <a-checkbox :indeterminate="isIndeterminate" :checked="isAllChecked"
                        @change="onCheckAllChange">全选</a-checkbox>
                    <span class="stat">本页{{ pageData.length }}条，共{{ total }}条</span>
                </div>

                <div class="footer-right">
                    <a-pagination :current="pageState.current" :page-size="pageState.pageSize" :total="total"
                        show-quick-jumper @change="onPageChange" class="mr8" />
                    <a-select v-model:value="pageState.pageSize" style="width: 80px" @change="onPageSizeChange"
                        :options="pageSizeOptions" />
                </div>
            </div>
        </div>

        <!-- ================= 详情抽屉 ================= -->
        <a-drawer v-model:visible="detailVisible" :width="drawerWidth" placement="right" :closable="false"
            @close="closeDetail" :body-style="{ padding: '0' }" :destroyOnClose="true">
            <!-- 抽屉头部 -->
            <div class="drawer-header">
                <a-typography-link class="back" @click="closeDetail">返回</a-typography-link>
                <span class="title">{{ detail.keyword || '—' }}</span>
            </div>

            <div class="drawer-body">
                <!-- 趋势图卡片 -->
                <a-card size="small" class="card">
                    <template #title>查询次数趋势</template>
                    <template #extra>
                        <a-space class="trend-tools" :size="8" align="center" wrap>
                            <a-radio-group v-model:value="detailRange" size="small" @change="onDetailRadioChange">
                                <a-radio-button value="24h">24小时</a-radio-button>
                                <a-radio-button value="7d">7天</a-radio-button>
                                <a-radio-button value="30d">30天</a-radio-button>
                            </a-radio-group>
                            <a-range-picker v-model:value="detailCustomRange" format="YYYY-MM-DD" size="small"
                                @change="onDetailRangePicked" />
                        </a-space>
                    </template>

                    <!-- ECharts 容器 -->
                    <div class="chart-box">
                        <div ref="chartRef" class="echart"></div>
                        <div class="chart-footer">统计时间：{{ nowText }}</div>
                    </div>
                </a-card>

                <!-- 意图与分词结果 -->
                <a-card size="small" class="card">
                    <div class="tag-line">
                        <span class="tag-label">意图识别结果：</span>
                        <a-tag color="gold">知识问答</a-tag>
                    </div>
                    <div class="tag-line mt8">
                        <span class="tag-label">分词结果：</span>
                        <a-space wrap>
                            <a-tag>向量索引</a-tag>
                            <a-tag>配置</a-tag>
                            <a-tag>召回阈值</a-tag>
                            <a-tag>推荐</a-tag>
                        </a-space>
                    </div>
                </a-card>

                <!-- 数据概览 -->
                <a-card size="small" class="card">
                    <template #title>数据概览</template>
                    <a-table size="small" :data-source="metrics" :columns="metricColumns" row-key="name"
                        :pagination="false" bordered>
                        <template #bodyCell="{ column, record }">
                            <template v-if="column.key === 'trend'">
                                <span :class="{ up: record.trendSign === 'up', down: record.trendSign === 'down' }">
                                    {{ record.trend }}
                                </span>
                            </template>
                        </template>
                    </a-table>
                </a-card>

                <!-- 召回卡片点击 -->
                <a-card size="small" class="card">
                    <template #title>召回卡片点击</template>
                    <a-table size="small" :data-source="cardClicks" :columns="cardClickColumns" row-key="id"
                        :pagination="false" bordered />
                </a-card>

                <!-- 普通词条点击 -->
                <a-card size="small" class="card">
                    <template #title>普通词条点击</template>
                    <a-table size="small" :data-source="normalClicks" :columns="normalClickColumns" row-key="rank"
                        :pagination="false" bordered>
                        <template #bodyCell="{ column, record }">
                            <template v-if="column.key === 'rank'"><span class="rank">{{ record.rank
                                    }}</span></template>
                            <template v-else-if="column.dataIndex === 'title'">
                                <a-typography-text ellipsis :title="record.title">{{ record.title }}</a-typography-text>
                            </template>
                            <template v-else-if="column.dataIndex === 'count'">
                                <span style="float:right">{{ toThousands(record.count) }}</span>
                            </template>
                        </template>
                    </a-table>
                </a-card>

                <!-- 推荐点击 -->
                <a-card size="small" class="card">
                    <template #title>推荐点击</template>
                    <a-table size="small" :data-source="recommendClicks" :columns="recommendClickColumns" row-key="rank"
                        :pagination="false" bordered>
                        <template #bodyCell="{ column, record }">
                            <template v-if="column.key === 'rank'"><span class="rank">{{ record.rank
                                    }}</span></template>
                            <template v-else-if="column.dataIndex === 'title'">
                                <a-typography-text ellipsis :title="record.title">{{ record.title }}</a-typography-text>
                            </template>
                            <template v-else-if="column.dataIndex === 'count'">
                                <span style="float:right">{{ toThousands(record.count) }}</span>
                            </template>
                        </template>
                    </a-table>
                </a-card>

                <!-- 底部操作 -->
                <div class="drawer-bottom-actions">
                    <a-typography-link class="op-link" @click="onCard(detail)">卡片</a-typography-link>
                    <span class="sep">|</span>
                    <a-typography-link class="op-link" @click="onSort(detail)">排序</a-typography-link>
                </div>
            </div>
        </a-drawer>
    </div>
</template>

<script setup lang="ts">
import { ref, reactive, computed, onMounted, onBeforeUnmount, watch, nextTick } from 'vue'
import type { TableColumnsType } from 'ant-design-vue'
import dayjs, { Dayjs } from 'dayjs'

import * as echarts from 'echarts/core'
import { LineChart } from 'echarts/charts'
import { GridComponent, TooltipComponent } from 'echarts/components'
import { CanvasRenderer } from 'echarts/renderers'
echarts.use([LineChart, GridComponent, TooltipComponent, CanvasRenderer])

/* ======================== 顶部搜索与筛选 ======================== */
const searchKeyword = ref<string>('')
const searchLoadingLock = ref(false)
const loading = ref(false)

const timeRange = ref<'24h' | '7d' | '30d' | 'custom'>('24h')
const customRange = ref<[Dayjs, Dayjs]>([dayjs().subtract(1, 'day'), dayjs()])
const onTimeRangeChange = () => { }
const applyCustomRange = () => { }

/* ======================== 列表数据与表头 ======================== */
type Row = { id: number; keyword: string; searchCount: number; recall: number; ctr: string; hit: number }
const allData = ref<Row[]>([
    { id: 1, keyword: '向量索引配置中召回阈值推荐', searchCount: 5488, recall: 90594, ctr: 'xx.xx%', hit: 5 },
    { id: 2, keyword: '军队文明2026新公告', searchCount: 3123, recall: 60560, ctr: 'xx.xx%', hit: 3 },
    { id: 3, keyword: '国防科技', searchCount: 2222, recall: 57865, ctr: 'xx.xx%', hit: 0 },
    { id: 4, keyword: '杀毒', searchCount: 445, recall: 4183, ctr: 'xx.xx%', hit: 3 },
    { id: 5, keyword: '美菲协议基地', searchCount: 444, recall: 55548, ctr: 'xx.xx%', hit: 5 },
    { id: 6, keyword: '2025-jq02-#1180', searchCount: 443, recall: 62673, ctr: 'xx.xx%', hit: 5 },
    { id: 7, keyword: '抗战胜利', searchCount: 442, recall: 70881, ctr: 'xx.xx%', hit: 5 },
    { id: 8, keyword: '73571584888835', searchCount: 441, recall: 18979, ctr: 'xx.xx%', hit: 3 },
    { id: 9, keyword: '国防科技大学官网的主办单位', searchCount: 410, recall: 60639, ctr: 'xx.xx%', hit: 3 },
    { id: 10, keyword: '机器学习算法', searchCount: 321, recall: 25317, ctr: 'xx.xx%', hit: 0 }
])

const columns = ref<TableColumnsType>([
    { title: '排名', key: 'rank', width: 80, align: 'center' },
    { title: '查询词', dataIndex: 'keyword', ellipsis: true },
    { title: '查询次数', key: 'searchCount', width: 120, align: 'right', sorter: (a: Row, b: Row) => a.searchCount - b.searchCount },
    { title: '召回数量', key: 'recall', width: 130, align: 'right', sorter: (a: Row, b: Row) => a.recall - b.recall },
    { title: '点击率', key: 'ctr', width: 110, align: 'center' },
    { title: '卡片命中', key: 'hit', width: 110, align: 'center', sorter: (a: Row, b: Row) => a.hit - b.hit },
    { title: '操作', key: 'actions', width: 160, align: 'center', fixed: 'right' }
])

/* 过滤 + 分页 */
const filteredData = computed(() => {
    const kw = searchKeyword.value.trim()
    if (!kw) return allData.value
    return allData.value.filter(r => r.keyword.toLowerCase().includes(kw.toLowerCase()))
})
const pageState = reactive({ current: 1, pageSize: 10 })
const total = computed(() => filteredData.value.length)
const pageData = computed(() => {
    const start = (pageState.current - 1) * pageState.pageSize
    return filteredData.value.slice(start, start + pageState.pageSize)
})
const pageSizeOptions = [
    { label: '10条', value: 10 }, { label: '20条', value: 20 }, { label: '30条', value: 30 }, { label: '50条', value: 50 }
]
const onPageChange = (p: number) => (pageState.current = p)
const onPageSizeChange = () => { pageState.current = 1 }

/* 搜索交互 */
const onSearchClick = () => { searchLoadingLock.value = true; pageState.current = 1; setTimeout(() => (searchLoadingLock.value = false), 200) }
const onInputChange = (e: Event) => { const v = (e.target as HTMLInputElement).value; if (!v) pageState.current = 1 }

/* 横向滚动 */
const tableScroll = computed(() => ({ x: 880 }))

/* 选择行 */
const selectedRowKeys = ref<(string | number)[]>([])
const rowSelection = computed(() => ({ selectedRowKeys: selectedRowKeys.value, onChange: (keys: (string | number)[]) => (selectedRowKeys.value = keys) }))
const isAllChecked = computed(() => pageData.value.length > 0 && pageData.value.every(r => selectedRowKeys.value.includes(r.id)))
const isIndeterminate = computed(() => selectedRowKeys.value.length > 0 && !isAllChecked.value)
const onCheckAllChange = (e: any) => {
    const checked = e.target.checked
    const ids = pageData.value.map(r => r.id)
    selectedRowKeys.value = checked
        ? Array.from(new Set([...(selectedRowKeys.value as (number | string)[]), ...ids]))
        : selectedRowKeys.value.filter(k => !ids.includes(k as number))
}

/* ======================== 详情抽屉状态 ======================== */
const detailVisible = ref(false)
const detail = reactive({ id: 0, keyword: '', searchCount: 0, recall: 0, ctr: '0.00%', hit: 0 })

const drawerWidth = ref(calcDrawerWidth())
function calcDrawerWidth() { const w = window.innerWidth; return w > 1280 ? 720 : Math.min(720, Math.floor(w * 0.86)) }
function onResize() { drawerWidth.value = calcDrawerWidth(); chartInstance?.resize() }
onMounted(() => window.addEventListener('resize', onResize))
onBeforeUnmount(() => { window.removeEventListener('resize', onResize); disposeChart() })

const detailRange = ref<'24h' | '7d' | '30d' | 'custom'>('24h')
const detailCustomRange = ref<[Dayjs, Dayjs]>([dayjs().subtract(7, 'day'), dayjs()])
const nowText = computed(() => dayjs().format('YYYY-MM-DD HH:mm:ss'))

/* 单选切换 */
const onDetailRadioChange = (payload: any) => {
    const v = typeof payload === 'string' ? payload : (payload?.target?.value ?? payload)
    if (v !== 'custom') {
        detailRange.value = v
        renderChart()
    }
}
/* 选择器选择日期范围 */
const onDetailRangePicked = () => {
    detailRange.value = 'custom'
    renderChart()
}

/* ======================== ECharts 折线图 ======================== */
import type { ECharts } from 'echarts'
const chartRef = ref<HTMLDivElement | null>(null)
let chartInstance: ECharts | null = null
function disposeChart() { if (chartInstance) { chartInstance.dispose(); chartInstance = null } }

/* 模拟数据 */
function genYearSeries() {
    let startYear: number, endYear: number
    if (detailRange.value === 'custom') { startYear = detailCustomRange.value[0].year(); endYear = detailCustomRange.value[1].year() }
    else { endYear = dayjs().year(); startYear = endYear - 9 }
    const years: string[] = []
    const values: number[] = []
    const base = detail.searchCount > 0 ? detail.searchCount : 3000
    for (let y = startYear; y <= endYear; y++) {
        years.push(String(y))
        const i = y - startYear
        const noise = Math.sin(i / 2.8) * 500 + Math.cos(i / 3.7) * 300 + (Math.random() - 0.5) * 180
        values.push(Math.max(0, Math.round(base * 0.6 + noise)))
    }
    return { years, values }
}

function renderChart() {
    nextTick(() => {
        if (!chartRef.value) return
        if (!chartInstance) chartInstance = echarts.init(chartRef.value)

        const { years, values } = genYearSeries()
        const option: echarts.EChartsOption = {
            grid: { left: 25, right: 44, top: 36, bottom: 6, containLabel: true },
            xAxis: {
                type: 'category', data: years, boundaryGap: false,
                axisTick: { alignWithLabel: true, show: true },
                axisLabel: { color: '#666', interval: 0, showMaxLabel: true, hideOverlap: false, margin: 12 },
                splitLine: { show: false }, name: '年份', nameLocation: 'end', nameGap: 10, nameTextStyle: { color: '#999' }
            },
            yAxis: {
                type: 'value',
                axisLine: { show: true },
                axisLabel: {
                    color: '#666',
                    showMaxLabel: true,
                    margin: 10,
                    formatter: (v: number) => toThousands(v as number)
                },
                splitLine: { show: true, lineStyle: { color: '#e5e7f0' } },
                name: '量数', nameLocation: 'end', nameGap: 16, nameTextStyle: { color: '#999', padding: [0, 0, 0, -12] }
            },
            tooltip: {
                trigger: 'axis', axisPointer: { type: 'line' }, confine: true,
                formatter: (params: any) => { const p = params[0]; return `${toThousands(p.data)}<br/>${p.axisValue}` }
            },
            series: [{
                type: 'line', data: values, smooth: true, showSymbol: false,
                lineStyle: { width: 2.2, color: '#1f4fe0' },
                areaStyle: {
                    color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                        { offset: 0, color: 'rgba(31,79,224,0.40)' },
                        { offset: 0.5, color: 'rgba(31,79,224,0.24)' },
                        { offset: 1, color: 'rgba(31,79,224,0.08)' }
                    ])
                }
            }]
        }
        chartInstance.setOption(option)
        chartInstance.resize()
    })
}

/* 抽屉开/关 */
watch(detailVisible, v => { if (v) renderChart(); else disposeChart() })

/* ======================== 概览/点击 ======================== */
type MetricRow = { name: string; current: string | number; avg: string | number; trend: string; trendSign: 'up' | 'down' | 'flat' }
const metrics = ref<MetricRow[]>([])
const metricColumns: TableColumnsType = [
    { title: '指标', dataIndex: 'name', key: 'name', width: 160 },
    { title: '当前', dataIndex: 'current', key: 'current', align: 'right', width: 120 },
    { title: '均值', dataIndex: 'avg', key: 'avg', align: 'right', width: 120 },
    { title: '趋势', dataIndex: 'trend', key: 'trend', align: 'right', width: 120 }
]

/* 召回卡片点击 */
const cardClicks = ref([{ id: 1, pos: '位置A', title: '卡片标题示例A', click: 35 }, { id: 2, pos: '位置B', title: '卡片标题示例B', click: 18 }])
const cardClickColumns: TableColumnsType = [
    { title: '位置', dataIndex: 'pos', key: 'pos', width: 120 },
    { title: '卡片标题', dataIndex: 'title', key: 'title' },
    { title: '点击次数', dataIndex: 'click', key: 'click', align: 'right', width: 120 }
]

/* 普通词条点击 */
type NormalClickRow = { rank: number; title: string; count: number }
const normalClicks = ref<NormalClickRow[]>([
    { rank: 1, title: 'Dify 中的 RAG 技术与企业级案例应用实战', count: 20 },
    { rank: 2, title: 'Dify.AI向量搜索:相似度匹配优化', count: 19 },
    { rank: 3, title: '「milvus检索」 milvus检索召回率_向量库milvus召回不准', count: 18 },
    { rank: 4, title: '响应曲面方法可以找到响应变量最', count: 17 },
    { rank: 5, title: '任务方案思考:句子相似度和匹配', count: 9 },
    { rank: 6, title: '数据永不丢失:FAISS向量索引备份与恢复实战指南', count: 8 },
    { rank: 7, title: 'Chroma向量索引技术:HNSW算法优化实践', count: 7 },
    { rank: 8, title: '向量检索推荐的最佳设置方法', count: 6 },
    { rank: 9, title: 'RAG系统性能提升10倍的秘密:向量数据库优化与智能...', count: 5 },
    { rank: 10, title: 'Dify中的RAG和知识库', count: 4 }
])
const normalClickColumns: TableColumnsType = [
    { title: '排名', key: 'rank', width: 70, align: 'center' },
    { title: '推荐词', dataIndex: 'title' },
    { title: '点击次数', dataIndex: 'count', align: 'right', width: 120 }
]

/* 推荐点击 */
type RecommendClickRow = { rank: number; title: string; count: number }
const recommendClicks = ref<RecommendClickRow[]>([
    { rank: 1, title: '系引超出矩阵维度原因matlab', count: 5488 },
    { rank: 2, title: '召回率函数训练图像', count: 123 },
    { rank: 3, title: '回应阈值', count: 231 },
    { rank: 4, title: '响应曲面方法可以找到响应变量最', count: 0 },
    { rank: 5, title: '向量召回爆搜玩法', count: 0 }
])
const recommendClickColumns: TableColumnsType = [
    { title: '排名', key: 'rank', width: 70, align: 'center' },
    { title: '推荐词', dataIndex: 'title' },
    { title: '点击次数', dataIndex: 'count', align: 'right', width: 120 }
]

/* ======================== 行点击 ======================== */
const onCustomRow = (record: Row) => ({ onClick: () => onDetail(record), class: 'clickable-row' })
const onDetail = (row: Row) => {
    detail.id = row.id; detail.keyword = row.keyword; detail.searchCount = row.searchCount
    detail.recall = row.recall; detail.ctr = row.ctr; detail.hit = row.hit
    metrics.value = [
        { name: '查询次数', current: toThousands(row.searchCount), avg: toThousands(4488), trend: '+16.45%', trendSign: 'up' },
        { name: '词条点击次数', current: 564, avg: 358, trend: '+16.45%', trendSign: 'up' },
        { name: '词条无点击率', current: '16.01%', avg: '36.54%', trend: '+16.45%', trendSign: 'up' },
        { name: '推荐点击次数', current: 65, avg: 78, trend: '-16.45%', trendSign: 'down' },
        { name: '停留时长', current: '00:01:11', avg: '00:00:45', trend: '+16.45%', trendSign: 'up' },
        { name: '召回数据量', current: toThousands(row.recall), avg: toThousands(5687121), trend: '-82.36%', trendSign: 'down' },
        { name: '召回卡片', current: 2, avg: 2, trend: '+0', trendSign: 'flat' },
        { name: '排序记录', current: 1, avg: 0, trend: '+1', trendSign: 'up' }
    ]
    detailVisible.value = true
}

/* 底部卡片 | 排序 */
const onCard = (row: any) => onDetail(typeof row?.id !== 'undefined' ? (row as Row) : allData.value[0])
const onSort = (row: any) => onDetail(typeof row?.id !== 'undefined' ? (row as Row) : allData.value[0])

/* ======================== 导出 CSV ======================== */
const toThousands = (n: number | string) => {
    const s = String(n)
    const [int, dec] = s.split('.')
    const intWithCommas = int.replace(/\B(?=(\d{3})+(?!\d))/g, ',')
    return dec ? `${intWithCommas}.${dec}` : intWithCommas
}
const onExport = () => {
    const header = ['排名', '查询词', '查询次数', '召回数量', '点击率', '卡片命中']
    const rows = filteredData.value.map((r, i) => [i + 1, `"${r.keyword.replace(/"/g, '""')}"`, r.searchCount, r.recall, r.ctr, r.hit])
    const csv = [header.join(','), ...rows.map(r => r.join(','))].join('\n')
    const blob = new Blob([csv], { type: 'text/csv;charset=utf-8;' })
    const url = URL.createObjectURL(blob)
    const a = document.createElement('a'); a.href = url; a.download = '搜索热词.csv'; a.click(); URL.revokeObjectURL(url)
}
</script>

<style scoped lang="less">
.hotkeys-page {
    min-height: 100%;
    padding: 12px;
    display: flex;
    flex-direction: column;
    gap: 12px;
    background: #fff;
}

/* 顶部工具条 */
.toolbar {
    display: flex;
    align-items: center;
    justify-content: space-between;
    gap: 12px;
    flex-wrap: wrap;

    .left-group {
        display: flex;
        align-items: center;
        gap: 12px;
        flex-wrap: wrap;
    }

    .right-group {
        display: flex;
        align-items: center;
        gap: 8px;
    }

    .search-wrap {
        display: flex;
        align-items: center;
        gap: 8px;

        .search-input {
            width: clamp(220px, 26vw, 360px);
        }

        .search-btn {
            white-space: nowrap;
        }
    }

    .label {
        color: #666;
    }

    .time-radio {
        :deep(.ant-radio-button-wrapper) {
            height: 32px;
            line-height: 30px;
            padding: 0 14px;
        }
    }

    .custom-range {
        display: flex;
        align-items: center;

        .ml8 {
            margin-left: 8px;
        }
    }
}

/* 列表卡片与表格 */
.table-card {
    flex: 1;
    display: flex;
    flex-direction: column;
    gap: 8px;
    overflow: hidden;

    .hot-table :deep(.ant-table) {

        .ant-table-thead>tr>th,
        .ant-table-tbody>tr>td {
            padding: 8px 16px;
            white-space: nowrap;
        }

        .ant-table-thead>tr>th {
            background: #fafafa;
        }
    }

    .rank {
        display: inline-block;
        min-width: 24px;
        text-align: center;

        &.top3 {
            font-weight: 600;
            color: #1677ff;
        }
    }

    .table-footer {
        display: flex;
        align-items: center;
        justify-content: space-between;
        gap: 12px;
        flex-wrap: wrap;
        padding: 8px 4px;

        .footer-left {
            display: flex;
            align-items: center;
            gap: 12px;

            .stat {
                color: #666;
            }
        }

        .footer-right {
            display: flex;
            align-items: center;
            gap: 8px;

            .mr8 {
                margin-right: 8px;
            }
        }
    }
}

/* 抽屉样式 */
.drawer-header {
    height: 52px;
    display: flex;
    align-items: center;
    padding: 0 16px;
    border-bottom: 1px solid #f0f0f0;

    .back {
        margin-right: 12px;
    }

    .title {
        font-size: 16px;
        font-weight: 600;
    }
}

.drawer-body {
    padding: 12px;
    background: #fff;
}

.card+.card {
    margin-top: 12px;
}

/* 图表容器 */
.chart-box {
    .echart {
        height: 320px;
        width: 100%;
    }

    .chart-footer {
        margin-top: 8px;
        color: #999;
        text-align: right;
        font-size: 12px;
    }
}

/* 抽屉底部 */
.drawer-bottom-actions {
    margin-top: 12px;
    padding: 10px 12px;
    border-top: 1px solid #eaeaea;
    display: flex;
    align-items: center;
    gap: 8px;

    .sep {
        color: #1677ff;
    }
}

.hot-table :deep(.op-link),
.hot-table :deep(.op-link:visited),
.hot-table :deep(.op-link:hover),
.hot-table :deep(.op-link:active),
.hot-table :deep(.op-link:focus),
.drawer-bottom-actions .op-link {
    color: #1677ff !important;
}

/* 小标签 */
.tag-line {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    gap: 8px;

    .tag-label {
        color: #666;
    }
}

.mt8 {
    margin-top: 8px;
}

.up {
    color: #ff4d4f;
}

.down {
    color: #52c41a;
}

/* 行交互 */
:deep(.clickable-row) {
    cursor: pointer;
}

:deep(.clickable-row:hover) {
    background: #f5faff;
}

/* 趋势工具条等高 */
.trend-tools {
    display: inline-flex;
    align-items: center;
    gap: 8px;

    :deep(.ant-radio-group-small .ant-radio-button-wrapper) {
        height: 24px;
        line-height: 22px;
        padding: 0 10px;
    }

    :deep(.ant-picker-small) {
        height: 24px;
    }

    :deep(.ant-picker-small .ant-picker-input > input) {
        height: 22px;
        line-height: 22px;
    }

    :deep(.ant-picker-small .ant-picker-suffix) {
        line-height: 22px;
    }
}

/* 响应式 */
@media (max-width:768px) {
    .toolbar .left-group {
        width: 100%;
    }

    .toolbar .search-wrap .search-input {
        width: 100%;
        min-width: 0;
    }
}

/* 渐隐动效 */
.fade-enter-active,
.fade-leave-active {
    transition: opacity .2s;
}

.fade-enter-from,
.fade-leave-to {
    opacity: 0;
}
</style>
